#include "apoctextgraphics.h" 
#include "apocstring.h"
#include "x86/apocx86.h"

namespace Apoc
{
  using namespace Math;

  namespace TextGraphics
  {
    UInt8 defaultTextAttribute = GREEN_FOREGROUND;
    UInt16 cursorPosition = 0;

    template <typename T>
    static inline T* GetStartVidMem()
    { return (T*)0xB8000; }

    template <typename T>
    static inline T* GetEndVidMem()
    { return (T*)0xBFFFF; }

    UInt GetNumRows()
    { return 25; } 

    UInt GetNumColumns()
    { return 80; }

    UInt8 GetDefaultTextAttribute()
    { return defaultTextAttribute; }

    void SetDefaultTextAttribute(UInt8 textAttribute)
    { defaultTextAttribute = textAttribute; }

    void Clear()
    {
      for (UInt* vm = GetStartVidMem<UInt>(); (UInt)vm < (UInt)GetEndVidMem<UInt>(); ++vm)
#if APOC_ARCH == APOC_ARCH_X86_32
        *vm = 0x07200720;
#elif APOC_ARCH == APOC_ARCH_X86_64
        *vm = 0x0720072007200720;
#endif
    }

    void PutString(const Int8* message)
    { PutString(message, GetDefaultTextAttribute()); }

    static inline void UpdateCursorPosition()
    {
      // Write cursor location low byte index to CRT index register
      X86::OUTB(0x03D4, 0x0F);

      // Write new low byte of cursor position to CRT data register
      X86::OUTB(0x3D5, static_cast<UInt8>((cursorPosition) & 0xFF));

      // Write cursor location high byte index to CRT index register
      X86::OUTB(0x03D4, 0x0E);

      // Write new high byte of cursor position to CRT data register
      X86::OUTB(0x3D5, static_cast<UInt8>((cursorPosition >> 8) & 0xFF));
    }

    void SetCursorPosition(UInt16 position)
    {
      cursorPosition = position;
      UpdateCursorPosition();
    }

    void SetCursorPosition(const Math::Vec2dui& position)
    {
      SetCursorPosition(position.x + position.y*GetNumColumns());
    }

    UInt16 GetCursorPosition()
    { return cursorPosition; }

    Int8* GetVidMemAtCursor()
    { return GetStartVidMem<Int8>() + cursorPosition*2; }

    void PutString(const Int8* message, UInt16 position, UInt8 textAttribute)
    {
      Int8* vm = GetStartVidMem<Int8>();
      vm += position*2;

      while (*message != '\0')
      {
	if (*message == '\n')
	{
	  vm -= (cursorPosition % GetNumColumns())*sizeof(UInt16);
	  vm += GetNumColumns()*sizeof(UInt16);
	  ++message;
	}
	else
	{
	  *vm++ = *message++;
	  *vm++ = textAttribute;
	}
      }
    }

    void PutString(const Int8* message, UInt8 textAttribute)
    {
      Int8* vm = GetStartVidMem<Int8>();
      vm += cursorPosition*2;

      while (*message != '\0')
      {
	#ifdef QEMU
        X86::OUTB(0x3F8, *message);
	#endif

	if (*message == '\n')
	{
	  cursorPosition -= (cursorPosition % GetNumColumns());
	  cursorPosition += GetNumColumns();
          vm = GetVidMemAtCursor();
	  ++message;
	}
	else
	{
	  *vm++ = *message++;
	  *vm++ = textAttribute;
	  ++cursorPosition;
	}
      }

      UpdateCursorPosition();
    }
  }
}
